/*!
\copyright  Copyright (c) 2020-2025 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\ingroup    kymera
\brief      Handles music processing chain

*/

#include "kymera_music_processing.h"
#include "kymera.h"
#include "kymera_ucid.h"
#include "kymera_data.h"
#include "kymera_setup.h"
#if defined(INCLUDE_MUSIC_PROCESSING)
#include "user_eq.h"
#endif
#include <chain.h>
#include <panic.h>
#include <operators.h>

static kymera_music_processing_event_callbacks_t *callbacks;

void Kymera_InitMusicProcessing(void)
{
    callbacks = NULL;
}

void Kymera_RegisterMusicProcessingCallbacks(kymera_music_processing_event_callbacks_t *event_callbacks)
{
    callbacks = event_callbacks;
}

bool Kymera_IsMusicProcessingPresent(void)
{
    bool music_processing_present = FALSE;
    if(Kymera_GetChainConfigs()->chain_music_processing_config_p0
#ifdef INCLUDE_DECODERS_ON_P1
        && Kymera_GetChainConfigs()->chain_music_processing_config_p1
#endif
        )
    {
        music_processing_present = TRUE;
    }
#ifdef ENABLE_TWM_STEREO
    else if((Kymera_GetChainConfigs()->chain_music_processing_config_p0 || Kymera_GetChainConfigs()->chain_music_processing_mono_config_p0)
#ifdef INCLUDE_DECODERS_ON_P1
            && (Kymera_GetChainConfigs()->chain_music_processing_config_p1 || Kymera_GetChainConfigs()->chain_music_processing_mono_config_p1)
#endif
           )
    {
        music_processing_present = TRUE;
    }
#endif /* ENABLE_TWM_STEREO */
    return music_processing_present;
}

static void kymera_CreateMusicProcessingChainOnProcessor(operator_processor_id_t processor_id)
{
    if(Kymera_IsMusicProcessingPresent())
    {
        kymeraTaskData *theKymera = KymeraGetTaskData();
        const chain_config_t * config = NULL;

        if(processor_id == OPERATOR_PROCESSOR_ID_0)
        {
            config = Kymera_GetChainConfigs()->chain_music_processing_config_p0;
#ifdef ENABLE_TWM_STEREO
            if(appKymera_IsStereoMirroringLeftOrRight(theKymera))
                config = Kymera_GetChainConfigs()->chain_music_processing_mono_config_p0;
#endif /* ENABLE_TWM_STEREO */
        }
        else if(processor_id == OPERATOR_PROCESSOR_ID_1)
        {
            config = Kymera_GetChainConfigs()->chain_music_processing_config_p1;
#ifdef ENABLE_TWM_STEREO
            if(appKymera_IsStereoMirroringLeftOrRight(theKymera))
                config = Kymera_GetChainConfigs()->chain_music_processing_mono_config_p1;
#endif /* ENABLE_TWM_STEREO */
        }
        else
        {
            Panic();
        }
        theKymera->chain_music_processing_handle = PanicNull(ChainCreate(config));
    }
}

static operator_processor_id_t kymera_IdentifyProcessorFromChainConfig(const chain_config_t * config)
{
    const operator_config_t * operator_config = config->operator_config;
    operator_processor_id_t processor_id = OPERATOR_PROCESSOR_ID_0;

    while(operator_config < (config->operator_config + config->number_of_operators))
    {
        if(operator_config->processor_id == OPERATOR_PROCESSOR_ID_1)
        {
            processor_id = OPERATOR_PROCESSOR_ID_1;
            break;
        }
        operator_config++;
    }
    return processor_id;
}

void Kymera_CreateMusicProcessingChain(void)
{
#if defined (INCLUDE_LE_AUDIO_BROADCAST_SOURCE) && defined (ENABLE_SIMPLE_SPEAKER) && defined (ENABLE_LE_AUDIO_TRANSCODE_BROADCAST_SUPPORT) 
    /* in-case speaker supports transcoding for broadcast concurrency use-case, then music processing
       should be in audio P0 to have correct Tx and Rx sync */
    operator_processor_id_t processor_id = OPERATOR_PROCESSOR_ID_0;
#else
    kymeraTaskData *theKymera = KymeraGetTaskData();
    PanicNull(theKymera->chain_input_handle);
    const chain_config_t * config = ChainGetConfig(theKymera->chain_input_handle);
    operator_processor_id_t processor_id = kymera_IdentifyProcessorFromChainConfig(config);
#endif /* defined (INCLUDE_LE_AUDIO_BROADCAST_SOURCE) && defined (ENABLE_SIMPLE_SPEAKER) && defined (ENABLE_LE_AUDIO_TRANSCODE_BROADCAST_SUPPORT) */

    kymera_CreateMusicProcessingChainOnProcessor(processor_id);
}

void Kymera_ConfigureMusicProcessing(uint32 sample_rate)
{
    if(Kymera_IsMusicProcessingPresent())
    {
        kymera_chain_handle_t chain = KymeraGetTaskData()->chain_music_processing_handle;
        Operator eq;
        Operator user_eq;

        PanicNull(chain);

        PanicFalse(Kymera_SetOperatorUcid(chain, OPR_ADD_HEADROOM, UCID_PASS_ADD_HEADROOM));
        PanicFalse(Kymera_SetOperatorUcid(chain, OPR_SPEAKER_EQ, UCID_SPEAKER_EQ));
        PanicFalse(Kymera_SetOperatorUcid(chain, OPR_REMOVE_HEADROOM, UCID_PASS_REMOVE_HEADROOM));


        eq = PanicZero(ChainGetOperatorByRole(chain, OPR_SPEAKER_EQ));
        OperatorsStandardSetSampleRate(eq, sample_rate);

        user_eq = ChainGetOperatorByRole(chain, OPR_USER_EQ);
        if(user_eq)
        {
            OperatorsStandardSetSampleRate(user_eq, sample_rate);
#if defined(INCLUDE_MUSIC_PROCESSING)
            UserEq_ApplyCurrentPresetAndGains();
#endif
        }

        if(KymeraGetTaskData()->chain_config_callbacks && KymeraGetTaskData()->chain_config_callbacks->ConfigureMusicProcessingChain)
        {
            kymera_music_processing_config_params_t params = {0};
            params.sample_rate = sample_rate;
            KymeraGetTaskData()->chain_config_callbacks->ConfigureMusicProcessingChain(chain, &params);
        }

        ChainConnect(chain);
    }
}

void Kymera_ConfigureMusicProcessing_AdditionalforAptxAdaptive(void)
{
#if defined(__QCC307X__) || defined(__QCC517X__)
    if(Kymera_IsMusicProcessingPresent())
    {
        kymera_chain_handle_t chain = KymeraGetTaskData()->chain_music_processing_handle;
        const chain_config_t * config = ChainGetConfig(chain);

        operator_processor_id_t processor_id = kymera_IdentifyProcessorFromChainConfig(config);
        if (processor_id == OPERATOR_PROCESSOR_ID_0)
        {
            Operator bpt;
            bpt = PanicZero(ChainGetOperatorByRole(chain, OPR_REMOVE_HEADROOM));
            OperatorsStandardSetBackKickThreshold(bpt, 0, 1, 0);
            bpt = PanicZero(ChainGetOperatorByRole(chain, OPR_ADD_HEADROOM));
            OperatorsStandardSetBackKickThreshold(bpt, 0, 1, 0);
        }
        else
        {
            Operator bpt;
            bpt = PanicZero(ChainGetOperatorByRole(chain, OPR_REMOVE_HEADROOM));
            OperatorsStandardSetBackKickThreshold(bpt, 0, 1, 0);
        }
    }
#endif
}

void Kymera_StartMusicProcessingChain(void)
{
    if(Kymera_IsMusicProcessingPresent())
    {
        kymeraTaskData *theKymera = KymeraGetTaskData();

        PanicNull(theKymera->chain_music_processing_handle);

        ChainStart(theKymera->chain_music_processing_handle);

        if(callbacks && callbacks->MusicProcessingStart)
        {
            callbacks->MusicProcessingStart();
        }
    }
}

void Kymera_StopMusicProcessingChain(void)
{
    if(Kymera_IsMusicProcessingPresent())
    {
        kymeraTaskData *theKymera = KymeraGetTaskData();

        PanicNull(theKymera->chain_music_processing_handle);

        ChainStop(theKymera->chain_music_processing_handle);

        if(callbacks && callbacks->MusicProcessingStop)
        {
            callbacks->MusicProcessingStop();
        }
    }
}

void Kymera_DestroyMusicProcessingChain(void)
{
    if(Kymera_IsMusicProcessingPresent())
    {
        kymeraTaskData *theKymera = KymeraGetTaskData();

        PanicNull(theKymera->chain_music_processing_handle);

        ChainDestroy(theKymera->chain_music_processing_handle);
        theKymera->chain_music_processing_handle = NULL;
    }
}

Operator Kymera_GetUserEqOperator(void)
{
    kymera_chain_handle_t chain = KymeraGetTaskData()->chain_music_processing_handle;
    const Operator user_eq_op = chain ? ChainGetOperatorByRole(chain, OPR_USER_EQ) : 0;

    return user_eq_op;
}
